package dynamic.wrapper.resultmap;



import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.github.yulichang.toolkit.KtUtils;
import com.github.yulichang.toolkit.MPJReflectionKit;

import com.github.yulichang.toolkit.support.FieldCache;
import com.github.yulichang.wrapper.resultmap.IResult;
import com.github.yulichang.wrapper.resultmap.MFunc;

import dynamic.metadata.DTableInfo;
import dynamic.metadata.EntityClass;
import dynamic.metadata.EntityProperty;
import dynamic.mybatisplus.core.toolkit.DTableHelper;
import dynamic.toolkit.support.DColumnCache;
import dynamic.wrapper.segments.DSelectCache;
import lombok.Getter;

import java.util.*;
import java.util.function.Predicate;

/**
 * 无泛型约束 实现自由映射
 *
 * @author yulichang
 * @since 1.4.6
 */

public class DMybatisLabelFree implements DLabel {

    private String property;

    private Class<?> javaType;

    private EntityClass ofType;

    private List<DIResult> resultList;

    /**
     * wrapper里面的引用
     */
    private List<DLabel> mybatisLabels;

    private DMybatisLabelFree() {
    }

    @Override
    public String getProperty() {
        return property;
    }

    @Override
    public Class<?> getJavaType() {
        return javaType;
    }

    @Override
    public EntityClass getOfType() {
        return ofType;
    }

    @Override
    public List<DIResult> getResultList() {
        return resultList;
    }

    @Override
    public List<?> getMybatisLabels() {
        return Collections.emptyList();
    }

    @SuppressWarnings({"unused", "DuplicatedCode"})
    public static class Builder {

        private final DMybatisLabelFree mybatisLabel;

        /**
         * 手动构建
         *
         * @param property property
         * @param javaType javaType
         * @param ofType   映射类
         */
        public Builder(String property, Class<?> javaType, EntityClass ofType) {
            this.mybatisLabel = new DMybatisLabelFree();
            mybatisLabel.property = property;
            mybatisLabel.javaType = javaType;
            mybatisLabel.ofType = ofType;
            mybatisLabel.resultList = new DResultList();
            mybatisLabel.mybatisLabels = new ArrayList<>();
        }

        public Builder all(EntityClass entityClass) {
            allBuild(null, entityClass);
            return this;
        }

        public DMybatisLabelFree.Builder all(String prefix, EntityClass entityClass) {
            allBuild(prefix, entityClass);
            return this;
        }

        /**
         * 映射实体字段过滤(含主键)
         */
        public DMybatisLabelFree.Builder filter(EntityClass entityClass, Predicate<DSelectCache> predicate) {
            Map<String, EntityProperty> fieldMap = mybatisLabel.ofType.getProperties();
            DColumnCache.getListField(entityClass).stream().filter(predicate)
                    .filter(p -> fieldMap.containsKey(p.getColumProperty())).forEach(c ->
                            mybatisLabel.resultList.add(new DResult.Builder(false, null, c).build()));
            return this;
        }

        /**
         * 映射实体字段过滤(含主键)
         */
        public DMybatisLabelFree.Builder filter(String prefix, EntityClass entityClass, Predicate<DSelectCache> predicate) {
            Map<String, EntityProperty> fieldMap =  mybatisLabel.ofType.getProperties();
            DColumnCache.getListField(entityClass).stream().filter(predicate)
                    .filter(p -> fieldMap.containsKey(p.getColumProperty())).forEach(c ->
                            mybatisLabel.resultList.add(new DResult.Builder(false, prefix, c).build()));
            return this;
        }

        public DMybatisLabelFree.Builder id(EntityProperty entity, EntityProperty tag) {
            DResult.Builder builder = new DResult.Builder(true, null);
            builder.column(entity).property(tag);
            mybatisLabel.resultList.add(builder.build());
            return this;
        }

        public DMybatisLabelFree.Builder id(EntityProperty entity) {
            DResult.Builder builder = new DResult.Builder(true, null);
            builder.column(entity);
            mybatisLabel.resultList.add(builder.build());
            return this;
        }

        public DMybatisLabelFree.Builder id(String index, EntityProperty entity, EntityProperty tag) {
            DResult.Builder builder = new DResult.Builder(true, index);
            builder.column(entity).property(tag);
            mybatisLabel.resultList.add(builder.build());
            return this;
        }

        public DMybatisLabelFree.Builder id(String index, EntityProperty entity) {
            DResult.Builder builder = new DResult.Builder(true, index);
            builder.column(entity);
            mybatisLabel.resultList.add(builder.build());
            return this;
        }

        public DMybatisLabelFree.Builder result(EntityProperty entity, EntityProperty tag) {
            DResult.Builder builder = new DResult.Builder(false, null);
            builder.column(entity).property(tag);
            mybatisLabel.resultList.add(builder.build());
            return this;
        }

        public DMybatisLabelFree.Builder result(EntityProperty entity) {
            DResult.Builder builder = new DResult.Builder(false, null);
            builder.column(entity);
            mybatisLabel.resultList.add(builder.build());
            return this;
        }

        public DMybatisLabelFree.Builder result(String index, EntityProperty entity, EntityProperty tag) {
            DResult.Builder builder = new DResult.Builder(false, index);
            builder.column(entity).property(tag);
            mybatisLabel.resultList.add(builder.build());
            return this;
        }

        public DMybatisLabelFree.Builder result(String index, EntityProperty entity) {
            DResult.Builder builder = new DResult.Builder(false, index);
            builder.column(entity);
            mybatisLabel.resultList.add(builder.build());
            return this;
        }

        public DMybatisLabelFree.Builder collection(EntityClass entityClass, EntityProperty func) {
            return collection(null, entityClass, func);
        }

        /**
         * 嵌套
         */
        public DMybatisLabelFree.Builder collection(String prefix, EntityClass entityClass, EntityProperty func) {
            String dtoFieldName = DColumnCache.getName(func);
            Map<String, EntityProperty> fieldMap = func.getEntityClass().getProperties();
            EntityProperty field = fieldMap.get(dtoFieldName);
            EntityClass genericType = field.getGenericType();
            DMybatisLabel.Builder builder;
            if (genericType == null || genericType.isAssignableFrom(entityClass)) {
                //找不到集合泛型 List List<?> List<Object> ， 直接查询数据库实体
                builder = new DMybatisLabel.Builder(prefix, dtoFieldName, entityClass, field.getPropertyClass());
            } else {
                builder = new DMybatisLabel.Builder(prefix, dtoFieldName, entityClass, field.getPropertyClass(), genericType, true);
            }
            mybatisLabel.mybatisLabels.add(builder.build());
            return this;
        }

        public DMybatisLabelFree.Builder collection(EntityClass entityClass, EntityProperty func, MFunc<DMybatisLabel.Builder> mFunc) {
            return collection(null, entityClass, func, mFunc);
        }

        public DMybatisLabelFree.Builder collection(EntityProperty func, MFunc<DMybatisLabelFree.Builder> mFunc) {
            String dtoFieldName = func.getPropertyName();
//            FieldCache field = MPJReflectionKit.getFieldMap(KtUtils.ref(func)).get(dtoFieldName);
//            //获取集合泛型
//            Class<?> genericType = MPJReflectionKit.getGenericType(field.getField());
            EntityClass genericType = func.getGenericType();
            DMybatisLabelFree.Builder builder = new DMybatisLabelFree.Builder(dtoFieldName, func.getPropertyClass(), genericType);
            mybatisLabel.mybatisLabels.add(mFunc.apply(builder).build());
            return this;
        }

        /**
         * 嵌套
         */
        public DMybatisLabelFree.Builder collection(String prefix,
                                                                                        EntityClass entityClass,
                                                                                        EntityProperty func,
                                                                                        MFunc<DMybatisLabel.Builder> mFunc) {
            String dtoFieldName = func.getPropertyName();
//            FieldCache field = MPJReflectionKit.getFieldMap(KtUtils.ref(func)).get(dtoFieldName);
//            //获取集合泛型
//            Class<?> genericType = MPJReflectionKit.getGenericType(field.getField());
            EntityClass genericType = func.getGenericType();
            DMybatisLabel.Builder builder = new DMybatisLabel.Builder(prefix, dtoFieldName, entityClass, func.getPropertyClass(), genericType, false);
            mybatisLabel.mybatisLabels.add(mFunc.apply(builder).build());
            return this;
        }

        public DMybatisLabelFree.Builder association(EntityClass child, EntityProperty dtoField) {
            return association(null, child, dtoField);
        }

        /**
         * 嵌套
         */
        public DMybatisLabelFree.Builder association(String index, EntityClass child, EntityProperty dtoField) {
            Map<String, EntityProperty> fieldMap = dtoField.getEntityClass().getProperties();
            String dtoFieldName = dtoField.getPropertyName();
            EntityProperty field = fieldMap.get(dtoFieldName);
            Assert.isFalse(Collection.class.isAssignableFrom(field.getPropertyClass()), "association 不支持集合类");
            DMybatisLabel.Builder builder;
            builder = new DMybatisLabel.Builder(index, dtoFieldName, child, field.getPropertyClass(),  field.getEntityClass(), true);
            mybatisLabel.mybatisLabels.add(builder.build());
            return this;
        }

        public DMybatisLabelFree.Builder association(EntityClass child, EntityProperty dtoField,
                                                                                         MFunc<DMybatisLabel.Builder> collection) {
            return association(null, child, dtoField, collection);
        }

        /**
         * 嵌套
         */
        public DMybatisLabelFree.Builder association(String index, EntityClass child, EntityProperty dtoField,
                                                                                         MFunc<DMybatisLabel.Builder> collection) {
            String dtoFieldName = dtoField.getPropertyName();
            Assert.isFalse(Collection.class.isAssignableFrom(dtoField.getPropertyClass()), "association 不支持集合类");
            DMybatisLabel.Builder builder = new DMybatisLabel.Builder(index, dtoFieldName, child, dtoField.getPropertyClass(),  dtoField.getGenericType(), false);
            mybatisLabel.mybatisLabels.add(collection.apply(builder).build());
            return this;
        }

        public DMybatisLabelFree build() {
            if (CollectionUtils.isEmpty(mybatisLabel.resultList)) {
                DTableInfo tableInfo = DTableHelper.get(mybatisLabel.ofType);
                Assert.notNull(tableInfo,
                        "无法自动映射, 找不到 <%s> 对应的表, 请使用 .all(xxx.class), id()或者result() 手动映射",
                        mybatisLabel.ofType.getSimpleName());
                all(mybatisLabel.ofType);
            }
            return mybatisLabel;
        }

        private void allBuild(String prefix, EntityClass entityClass) {
            Map<String, EntityProperty> tagMap = entityClass.getProperties();
            List<DSelectCache> listField = DColumnCache.getListField(entityClass);
            for (DSelectCache s : listField) {
                EntityProperty field = tagMap.get(s.getColumProperty());
                if (Objects.nonNull(field)) {
                    DResult result = new DResult();
                    result.setIndex(prefix);
                    result.setId(s.isPk());
                    result.setJavaType(field.getPropertyClass());
                    result.setProperty(s.getColumProperty());
                    result.setSelectNormal(s);
                    mybatisLabel.resultList.add(result);
                }
            }
        }
    }
}

